package gsw.tool.pluggable.am;

import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ComponentInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.text.TextUtils;
import android.util.Log;

import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import gsw.tool.pluggable.core.Env;

/**
 * 维护一个插件进程集合，用来管理所有的插件进程。
 */
public class StaticProcessList {
    private static final String TAG = "StaticProcessList";
    //缓存代理进程的名称与信息
    private Map<String, ProcessItem> items = new HashMap<String, ProcessItem>(5);
    //缓存非代理进程的名称与信息
    private List<String> mOtherProcessNames = new ArrayList<>();
    //组件信息的比较器，用来比较两个组件是否一致
    private static final Comparator<ComponentInfo> sComponentInfoComparator = new Comparator<ComponentInfo>() {
        @Override
        public int compare(ComponentInfo lhs, ComponentInfo rhs) {
            return Collator.getInstance().compare(lhs.name, rhs.name);
        }
    };

    /**
     * 初始化items
     * 通过IntentFilter，从清单文件中查找代理组件的存档信息，并添加到items中。
     * 代理组件的action统一为Intent.ACTION_MAIN,Category统一为Env.CATEGORY_ACTIVITY_PROXY_STUB。
     *
     * @param context PMS服务的上下文
     */
    public void onCreate(Context context) {
        //根据代理组件的IntentFilter设置Intent
        Intent intent = new Intent(Intent.ACTION_MAIN);
        intent.addCategory(Env.CATEGORY_ACTIVITY_PROXY_STUB);
        //设置宿主APK的包名
        intent.setPackage(context.getPackageName());

        /**
         * 从宿主的PackageManager中Activity和BroadcastReceiver的存档信息
         */
        PackageManager pm = context.getPackageManager();
        List<ResolveInfo> activities = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA);
        for (ResolveInfo activity : activities) {
            addActivityInfo(activity.activityInfo);
        }

        /**
         * 从宿主的PackageManager件中Service的存档信息
         */
        List<ResolveInfo> services = pm.queryIntentServices(intent, 0);
        for (ResolveInfo service : services) {
            addServiceInfo(service.serviceInfo);
        }

        mOtherProcessNames.clear();
        PackageInfo packageInfo1 = null;
        try {
            packageInfo1 = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES
                    | PackageManager.GET_RECEIVERS
                    | PackageManager.GET_PROVIDERS
                    | PackageManager.GET_SERVICES);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        if (packageInfo1.activities != null) {
            for (ActivityInfo info : packageInfo1.activities) {
                if (!mOtherProcessNames.contains(info.processName) && !items.containsKey(info.processName)) {
                    mOtherProcessNames.add(info.processName);
                }
            }
        }

        if (packageInfo1.receivers != null) {
            for (ActivityInfo info : packageInfo1.receivers) {
                if (!mOtherProcessNames.contains(info.processName) && !items.containsKey(info.processName)) {
                    mOtherProcessNames.add(info.processName);
                }
            }
        }

        if (packageInfo1.providers != null) {
            for (ProviderInfo info : packageInfo1.providers) {
                if (!mOtherProcessNames.contains(info.processName) && !items.containsKey(info.processName)) {
                    mOtherProcessNames.add(info.processName);
                }
            }
        }

        if (packageInfo1.services != null) {
            for (ServiceInfo info : packageInfo1.services) {
                if (!mOtherProcessNames.contains(info.processName) && !items.containsKey(info.processName)) {
                    mOtherProcessNames.add(info.processName);
                }
            }
        }
    }

    /**
     * 添加ContentProvider的存档信息到对应进程的ProcessItem中
     *
     * @param info ContentProvider的存档信息
     */
    private void addProviderInfo(ProviderInfo info) {
        if (TextUtils.isEmpty(info.processName)) {
            info.processName = info.packageName;
        }
        ProcessItem item = items.get(info.processName);
        if (item == null) {
            item = new ProcessItem();
            item.name = info.processName;
            items.put(info.processName, item);
        }
        item.addProviderInfo(info);
    }

    /**
     * 添加Service的存档信息到对应进程的ProcessItem中
     *
     * @param info Service的存档信息
     */
    private void addServiceInfo(ServiceInfo info) {
        if (TextUtils.isEmpty(info.processName)) {
            info.processName = info.packageName;
        }
        ProcessItem item = items.get(info.processName);
        if (item == null) {
            item = new ProcessItem();
            item.name = info.processName;
            items.put(info.processName, item);
        }
        item.addServiceInfo(info);
    }

    /**
     * 添加Activity和BroadcastReceiver的存档信息到对应进程的ProcessItem中
     *
     * @param info Activity和BroadcastReceiver的存档信息
     */
    private void addActivityInfo(ActivityInfo info) {
        if (TextUtils.isEmpty(info.processName)) {
            info.processName = info.packageName;
        }
        ProcessItem item = items.get(info.processName);
        if (item == null) {
            item = new ProcessItem();
            item.name = info.processName;
            items.put(info.processName, item);
        }
        item.addActivityInfo(info);
    }

    /**
     * 根据进程名和组件名查找代理Activity的存档信息
     *
     * @param processName  进程名
     * @param activityName 组件名
     * @return 代理组件的存档信息
     */
    ActivityInfo findActivityInfoForName(String processName, String activityName) {
        Log.i(TAG, "findActivityInfoForName: ");
        ProcessItem item = items.get(processName);
        if (item != null && item.activityInfos != null) {
            return item.activityInfos.get(activityName);
        }
        return null;
    }

    /**
     * 根据进程名和启动模式查找代理Activity的存档信息
     *
     * @param processName 进程名
     * @param launchMode  启动模式
     * @return 代理组件的存档信息
     */
    ActivityInfo findActivityInfoForLaunchMode(String processName, int launchMode) {
        Log.i(TAG, "findActivityInfoForLaunchMode: ");
        ProcessItem item = items.get(processName);
        if (item != null && item.activityInfos != null) {
            for (ActivityInfo info : item.activityInfos.values()) {
                if (info.launchMode == launchMode) {
                    return info;
                }
            }
        }
        return null;
    }

    /**
     * 根据进程名查找代理Activity的存档信息
     *
     * @param processName 进程名
     * @return 代理组件的存档信息
     */
    List<ActivityInfo> getActivityInfoForProcessName(String processName) {
        Log.i(TAG, "getActivityInfoForProcessName: ");
        ProcessItem item = items.get(processName);
        ArrayList<ActivityInfo> activityInfos = new ArrayList<ActivityInfo>(item.activityInfos.values());
        //对集合进行排序，方便查找
        Collections.sort(activityInfos, sComponentInfoComparator);
        return activityInfos;
    }

    /**
     * 根据进程名和组件名查找代理Service的存档信息
     *
     * @param processName     进程名
     * @param serviceInfoName 组件名
     * @return 代理组件的存档信息
     */
    ServiceInfo findServiceInfoForName(String processName, String serviceInfoName) {
        Log.i(TAG, "findServiceInfoForName: ");
        ProcessItem item = items.get(processName);
        if (item != null && item.serviceInfos != null) {
            return item.serviceInfos.get(serviceInfoName);
        }
        return null;
    }

    /**
     * 根据进程名查找代理Service的存档信息
     *
     * @param processName 进程名
     * @return 代理组件的存档信息
     */
    List<ServiceInfo> getServiceInfoForProcessName(String processName) {
        Log.i(TAG, "getServiceInfoForProcessName: ");
        ProcessItem item = items.get(processName);
        ArrayList<ServiceInfo> serviceInfos = new ArrayList<ServiceInfo>(item.serviceInfos.values());
        Collections.sort(serviceInfos, sComponentInfoComparator);
        return serviceInfos;
    }

    /**
     * 根据进程名和组件名查找代理Provider的存档信息
     *
     * @param processName 进程名
     * @param authority   组件名
     * @return 代理组件的存档信息
     */
    ProviderInfo findProviderInfoForAuthority(String processName, String authority) {
        Log.i(TAG, "findProviderInfoForAuthority: ");
        ProcessItem item = items.get(processName);
        if (item != null && item.providerInfos != null) {
            return item.providerInfos.get(authority);
        }
        return null;
    }

    /**
     * 根据进程名查找代理Provider的存档信息
     *
     * @param processName 进程名
     * @return 代理组件的存档信息
     */
    List<ProviderInfo> getProviderInfoForProcessName(String processName) {
        Log.i(TAG, "getProviderInfoForProcessName: ");
        ProcessItem item = items.get(processName);
        ArrayList<ProviderInfo> providerInfos = new ArrayList<ProviderInfo>(item.providerInfos.values());
        Collections.sort(providerInfos, sComponentInfoComparator);
        return providerInfos;
    }

    /**
     * 获取代理进程的进程名的列表
     *
     * @return 代理进程的进程名的列表
     */
    List<String> getProcessNames() {
        return new ArrayList<String>(items.keySet());
    }

    /**
     * 获取非代理进程的进程名的列表
     *
     * @return 代理进程的进程名的列表
     */
    public List<String> getOtherProcessNames() {
        return mOtherProcessNames;
    }

    /**
     * 清空代理进程信息
     */
    void clear() {
        items.clear();
    }

    /**
     * 我们预注册组件的进程信息
     */
    private class ProcessItem {

        //进程名
        private String name;
        //key=ActivityInfo.name,value=ActivityInfo(包含BoradcastReceiver的存档信息)
        private Map<String, ActivityInfo> activityInfos = new HashMap<String, ActivityInfo>(4);
        //key=ServiceInfo.name,value=ServiceInfo
        private Map<String, ServiceInfo> serviceInfos = new HashMap<String, ServiceInfo>(1);
        //key=ProviderInfo.authority,value=ProviderInfo
        private Map<String, ProviderInfo> providerInfos = new HashMap<String, ProviderInfo>(1);

        private void addActivityInfo(ActivityInfo info) {
            if (!activityInfos.containsKey(info.name)) {
                activityInfos.put(info.name, info);
            }
        }

        private void addServiceInfo(ServiceInfo info) {
            if (!serviceInfos.containsKey(info.name)) {
                serviceInfos.put(info.name, info);
            }
        }

        private void addProviderInfo(ProviderInfo info) {
            if (!providerInfos.containsKey(info.authority)) {
                providerInfos.put(info.authority, info);
            }
        }
    }

}
